<template>
  <div class="com-db-hub">
    <div class="layout-h">
      <side-l ref="SideL"></side-l>
      <div class="auto-flex flex-v">
        <div class="glb">
          <div class="auto-flex cy">
            <span class="f16">
              <i class="glyphicon glyphicon-list-alt"></i>
              <strong @contextmenu="$root.copy(dbTable)">{{ dbTable.replaceAll('`', '') }}</strong>
            </span>
          </div>
          <div>
            <OpenFile></OpenFile>
            <div class="btn-group">
              <button
                v-for="(v, idx) in tab.list"
                :key="idx"
                :class="['btn btn-sm btn-' + (v.com === curTab ? 'primary' : 'default')]"
                @click="$root.updateRouter({ curTab: v.com })"
              >{{ v.name }}</button>
            </div>
          </div>
        </div>
        <Component class="auto-flex"
          :is="curTab"
          @lsData="lsData"
        ></Component>
      </div>
    </div>
  </div>
</template>

<script>
import { markRaw } from 'vue'
import SideL from './side-l.vue'
import TableQueryData from './table-query-data.vue'
import TableStructure from './table-structure.vue'
import TableProcedure from './table-procedure.vue'
import TableFunction from './table-function.vue'

export default {
  components: {
    SideL,
    TableQueryData,
    TableStructure,
    TableProcedure,
    TableFunction,
  },
  name: 'com-db-hub',
  data() {
    const tab = {
      list: [
        { name: '浏览数据', com: 'table-query-data' },
        { name: '库表结构', com: 'table-structure' },
        { name: '存储过程', com: 'table-procedure' },
        { name: '存储函数', com: 'table-function' },
      ],
      map: {}
    }
    tab.list.forEach(v => tab.map[v.com] = v)

    return {
      tab: markRaw(tab),
      def: {
        time: 0,
        serverLang: '',
        dbInfo: {},
        server: {},
        gs: [],
        vars: [],
        engines: {},
        charset: [],
        collation: [],
        php_ini: {},
      },
      db: {
        isLoading: true,
        sc: '',
        list: []
      },
      table: {
        isLoading: true,
        sc: {},
        list: []
      },
      field: {
        isLoading: true,
        list: []
      },
      fieldKeys: ['Field', 'Type', 'Null', 'Key', 'Default', 'Extra'],
      data: {
        isLoading: true,
        list: []
      },
      page: {
        cur: 1,
        size: 100,
        total: 0,
      }
    }
  },
  computed: {
    r() {
      return this.$root.router
    },
    curTab() {
      return this.r.curTab && this.tab.map[this.r.curTab] ? this.r.curTab : this.tab.list[0].com
    },
    scDB() {
      return this.db.sc
    },
    scTable() {
      return this.table.sc['Create Table'] ? this.table.sc['Create Table'] : this.$root.formatScTable(this.table.sc['Create View'])
    },
    curDB() {
      const curDB = this.r.curDB
      return curDB && this.db.list.some(v => v.name === curDB) ? curDB : this.db.list[0]?.name || ''
    },
    curTable() {
      const curTable = this.r.curTable
      return curTable && this.table.list.some(v => v.name === curTable) ? curTable : this.table.list[0]?.name || ''
    },
    dbTable() {
      if (!this.curDB) return '';
      return '`' + this.curDB + '`' + (this.curTable ? '.`' + this.curTable + '`' : '')
    },
    fields() {
      return this.field.list.filter(v => v.display)
    },
    keys() {
      const list = this.field.list
      const ks = ['PRI', 'UNI', 'MUL']
      let result = []

      for (let i = 0, len = ks.length; i < len; i++) {
        const kType = ks[i]
        result = list.filter(v => v.Key === kType)
        if (result.length > 0) break;
      }

      if (!result.length) result = list;

      return result.map(v => v.Field)
    },
  },
  watch: {
    async curDB() {
      this.page.total = 0
      this.lsTable()
    },
    async curTable() {
      this.page.total = 0
      this.lsField()
    },
    curTab() {
      this.lsField()
    },
  },
  methods: {
    async runSql(sql) {
      console.log('%crunSql', 'font-weight: bold; color: #09f', sql)
    },
    formatField(str) {
      if (!str || (str.length < 40 || !(str.indexOf(`'`) > -1))) return str;

      return str.replace(/\((.*?)\)/, (_, str) => {
        return `(\n${str.split(',').map((v) => '  ' + v).join(',\n')}\n)`
      })
    },
    formatField_(str) {
      if (!str || (str.length < 40 || !(str.indexOf(`'`) > -1))) return str;

      return str.replace(/\((.*?)\)/, (_, str) => {
        return `(\n${str.split(',').map((v) => '&nbsp;&nbsp;' + v).join(',\n')}\n)`.replace(/\n/g, '<br>')
      })
    },
    post(requestData, arg = {}) {
      const formData = new FormData()

      requestData = {
        dbHost: 'localhost',
        dbUser: 'root',
        dbPwd: 'root',
        // dbHost: 'qdm722898566.my3w.com',
        // dbUser: 'qdm722898566',
        // dbPwd: 'Tianshihua1',
        curDB: this.curDB,
        curTable: this.curTable,
        ...requestData
      }

      Object.keys(requestData).forEach((key) => {
        const val = requestData[key]
        if (val === undefined || val === null) return;
        formData.append(key, val)
      })

      const options = {
        method: 'POST',
        headers: {},
        body: formData
      }

      if (arg.signalFetch) {
        this.signalFetchController?.abort()
        this.signalFetchController = markRaw(new AbortController())
        options.signal = this.signalFetchController.signal
      }

      return fetch('api/db-hub.php', options).then(async (res) => {
        delete this.signalFetchController

        if (res.status !== 200) {
          const msg = `${res.url} | ${res.status} | ${res.statusText}`
          this.$messageBox.alert(msg, '提示', { type: 'error' })
          throw msg
        }
        return (await res.json()).data
      })
    },
    async lsDef() {
      await this.post({
        a: 'ls-def'
      }, {
        signalFetch: true
      }).then(async (data) => {
        this.def = data
        await this.lsDB()
      }).catch((e) => {
        console.error(e)
      })

      this.db.isLoading = false
      this.table.isLoading = false
      this.field.isLoading = false
      this.data.isLoading = false
    },
    async lsDB() {
      this.db.isLoading = true
      this.table.isLoading = true
      this.field.isLoading = true
      this.data.isLoading = true

      this.db.sc = ''
      this.db.list = []
      this.table.sc = {}
      this.table.list = []
      this.field.list = []
      this.data.list = []

      const signalLsDB = this.signalLsDB = Math.random()
      await sleep(5)
      if (signalLsDB !== this.signalLsDB) return new Promise(() => 0);

      await this.post({
        a: 'ls-db'
      }, {
        signalFetch: true
      }).then(async (data) => {
        if (signalLsDB !== this.signalLsDB) return new Promise(() => 0);

        this.db.list = data.map((name) => {
          return { name, checked: false }
        })
        await sleep(1)
        this.db.isLoading = false
        await this.lsTable()
      })

      this.db.isLoading = false
      this.table.isLoading = false
      this.field.isLoading = false
      this.data.isLoading = false
    },
    async lsTable() {
      const isLoading = !!this.curDB || this.db.isLoading
      this.table.isLoading = isLoading
      this.field.isLoading = isLoading
      this.data.isLoading = isLoading

      this.db.sc = ''
      this.table.sc = {}
      this.table.list = []
      this.field.list = []
      this.data.list = []

      if (this.db.isLoading || !this.curDB) return;

      const signalLsTable = this.signalLsTable = Math.random()
      await sleep(5)
      if (signalLsTable !== this.signalLsTable) return new Promise(() => 0);

      await this.post({
        a: 'ls-table'
      }, {
        signalFetch: true
      }).then(async (data) => {
        if (signalLsTable !== this.signalLsTable) return new Promise(() => 0);

        this.db.sc = data.scDB
        this.table.list = data.list.map((name) => {
          return {
            name,
            checked: false,
          }
        })
        await sleep(1)
        this.table.isLoading = false
        await this.lsField()
      })

      this.table.isLoading = false
      this.field.isLoading = false
      this.data.isLoading = false
    },
    async lsField() {
      const isLoading = !!this.curTable || this.table.isLoading
      this.field.isLoading = isLoading
      this.data.isLoading = isLoading

      this.table.sc = {}
      this.field.list = []
      this.data.list = []

      if (this.table.isLoading || !this.curTable || this.curTab !== 'table-query-data') return;

      const signalLsField = this.signalLsField = Math.random()
      await sleep(5)
      if (signalLsField !== this.signalLsField) return new Promise(() => 0);

      await this.post({
        a: 'ls-field',
      }, {
        signalFetch: true
      }).then(async (data) => {
        if (signalLsField !== this.signalLsField) return new Promise(() => 0);

        data.list.forEach((v) => {
          v.display = true
          v.sortCnt = 0
          v.sortTime = 0
          v.q = ''
        })

        this.table.sc = data.scTable
        this.field.list = data.list
        await sleep(1)
        this.field.isLoading = false
        await this.lsData()
      })

      this.field.isLoading = false
      this.data.isLoading = false
    },
    async lsData() {
      const isLoading = !!this.curTable || this.table.isLoading
      this.data.isLoading = isLoading
      this.data.list = []

      if (this.field.isLoading || !this.curTable || this.curTab !== 'table-query-data') return;
      
      const page = this.page
      const signalLsData = this.signalLsData = Math.random()
      await sleep(5)
      if (signalLsData !== this.signalLsData) return new Promise(() => 0);

      const where = this.fields.filter(v => v.q).map(v => [v.Field, v.q])
      const orderBy = [...this.fields].filter(v => v.sortCnt % 3 > 0).sort((a, b) => b.sortTime - a.sortTime).map(v => [v.Field, v.sortCnt % 3 - 1])

      await this.post({
        a: 'ls-data',
        pageCur: page.cur - 1,
        pageSize: page.size,
        ex: JSON.stringify({ where, orderBy })
      }, {
        signalFetch: true
      }).then(async (data) => {
        if (signalLsData !== this.signalLsData) return new Promise(() => 0);
        page.total = data.total
        this.data.list = data.list
      })

      this.data.isLoading = false
    },
  },
  async mounted() {
    window.com = this
    const vm = this.$root
    vm.dbHub = this
    await this.lsDef()
    ;[...document.querySelectorAll('.com-db-hub .list-db-table .on')].forEach((li) => {
      const wrapper = li.closest('.auto-flex')
      const pos = li.getBoundingClientRect()
      if (!(pos.top > window.innerHeight || pos.bottom < 0)) return;
      wrapper.scrollTop = li.offsetTop - 70
    })
  }
}
</script>

<style lang="less" scoped>
.com-db-hub {}
</style>
